﻿using Engine;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace Game
{
    /// <summary>
    /// 表达式运算器
    /// </summary>
    public class ExpressionCalculator
    {
        public class CompositeExpression
        {
            public string Expression;

            public List<string> Includes = new List<string>();

            public string[] Separations = new string[3];

            public int Rank;

            public float Result;
        }

        public static string[] Variables = new string[] { "x", "y", "z" };

        public static char[] Nums = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };

        public static string[] Funcs = new string[] { "sin", "cos", "tan", "ln", "abs", "^", "%" };

        public string Expression;

        public Dictionary<string, CompositeExpression> SubExpressions = new Dictionary<string, CompositeExpression>();

        public Dictionary<int, List<string>> Ranks = new Dictionary<int, List<string>>();

        public ExpressionCalculator(string expression)
        {
            Expression = expression;
            foreach (string func in Funcs)
            {
                SetFuncExpression(expression, func);
            }
            foreach (var ex1 in SubExpressions.Keys)
            {
                List<string> exs = new List<string>();
                foreach (var ex2 in SubExpressions.Keys)
                {
                    if (ex1 != ex2 && ex1.Contains(ex2))
                    {
                        exs.Add(ex2);
                    }
                }
                SubExpressions[ex1].Includes = exs;
            }
            SetExpressionRank();
        }

        public int Calculate(int x, int y, int z)
        {
            return Calculate(new int[] { x, y, z });
        }

        public int Calculate(int[] variableValue)
        {
            foreach (int ik in Ranks.Keys)
            {
                foreach (string rex in Ranks[ik])
                {
                    CompositeExpression composite = SubExpressions[rex];
                    string expression2 = composite.Separations[1];
                    foreach (string ci in composite.Includes)
                    {
                        if (float.IsNaN(SubExpressions[ci].Result))
                        {
                            return int.MinValue;
                        }
                        if (SubExpressions[ci].Result < 0)
                        {
                            expression2 = expression2.Replace(ci, "(0" + SubExpressions[ci].Result + ")");
                        }
                        else
                        {
                            expression2 = expression2.Replace(ci, SubExpressions[ci].Result + "");
                        }
                    }
                    string result = CalculateVariableExpression(expression2, Variables, variableValue);
                    float resultTemp = (float)double.Parse(result);
                    string expression3 = string.Empty;
                    float resultTemp2 = 1f;
                    if (composite.Separations[0] == "^" || composite.Separations[0] == "%")
                    {
                        expression3 = composite.Separations[2];
                        foreach (string ci in composite.Includes)
                        {
                            if (SubExpressions[ci].Result < 0)
                            {
                                expression3 = expression3.Replace(ci, "(0" + SubExpressions[ci].Result + ")");
                            }
                            else
                            {
                                expression3 = expression3.Replace(ci, SubExpressions[ci].Result + "");
                            }
                        }
                        result = CalculateVariableExpression(expression3, Variables, variableValue);
                        resultTemp2 = (float)double.Parse(result);
                    }
                    switch (composite.Separations[0])
                    {
                        case "sin": resultTemp = MathUtils.Sin(resultTemp); break;
                        case "cos": resultTemp = MathUtils.Cos(resultTemp); break;
                        case "tan": resultTemp = MathUtils.Tan(resultTemp); break;
                        case "ln": resultTemp = MathUtils.Log(resultTemp); break;
                        case "abs": resultTemp = MathUtils.Abs(resultTemp); break;
                        case "%": resultTemp = resultTemp % resultTemp2; break;
                        case "^": resultTemp = MathUtils.Pow(resultTemp, resultTemp2); break;

                    }
                    composite.Result = resultTemp;
                }
            }
            string expression4 = Expression;
            foreach (string ex in SubExpressions.Keys)
            {
                if (SubExpressions[ex].Result < 0)
                {
                    expression4 = expression4.Replace(ex, "(0" + SubExpressions[ex].Result + ")");
                }
                else
                {
                    expression4 = expression4.Replace(ex, SubExpressions[ex].Result + "");
                }
            }
            string result2 = CalculateVariableExpression(expression4, Variables, variableValue);
            float result3 = (float)double.Parse(result2);
            return (int)MathUtils.Round(result3);
        }

        //获取子表达式的层级并排序
        public void SetExpressionRank()
        {
            List<string> otsl1 = new List<string>();
            List<string> otsl2 = new List<string>();
            List<string> otsl3 = new List<string>();
            List<string> otsl4 = new List<string>();
            List<string> otsl5 = new List<string>();
            foreach (string ik in SubExpressions.Keys)
            {
                if (SubExpressions[ik].Includes.Count == 0)
                {
                    otsl1.Add(ik);
                    SubExpressions[ik].Rank = 1;
                }
            }
            foreach (string ik in SubExpressions.Keys)
            {
                if (SubExpressions[ik].Includes.Count > 0)
                {
                    if (otsl1.Contains(ik)) continue;
                    bool allContains = true;
                    foreach (string s in SubExpressions[ik].Includes)
                    {
                        if (!otsl1.Contains(s))
                        {
                            allContains = false;
                            break;
                        }
                    }
                    if (allContains)
                    {
                        otsl2.Add(ik);
                        SubExpressions[ik].Rank = 2;
                    }
                }
            }
            foreach (string ik in SubExpressions.Keys)
            {
                if (SubExpressions[ik].Includes.Count > 1)
                {
                    if (otsl2.Contains(ik) || otsl1.Contains(ik)) continue;
                    bool allContains = true;
                    foreach (string s in SubExpressions[ik].Includes)
                    {
                        if (!otsl2.Contains(s) && !otsl1.Contains(s))
                        {
                            allContains = false;
                            break;
                        }
                    }
                    if (allContains)
                    {
                        otsl3.Add(ik);
                        SubExpressions[ik].Rank = 3;
                    }
                }
            }
            foreach (string ik in SubExpressions.Keys)
            {
                if (SubExpressions[ik].Includes.Count > 2)
                {
                    if (otsl3.Contains(ik) || otsl2.Contains(ik) || otsl1.Contains(ik)) continue;
                    bool allContains = true;
                    foreach (string s in SubExpressions[ik].Includes)
                    {
                        if (!otsl3.Contains(s) && !otsl2.Contains(s) && !otsl1.Contains(s))
                        {
                            allContains = false;
                            break;
                        }
                    }
                    if (allContains)
                    {
                        otsl4.Add(ik);
                        SubExpressions[ik].Rank = 4;
                    }
                }
            }
            foreach (string ik in SubExpressions.Keys)
            {
                if (SubExpressions[ik].Includes.Count > 3)
                {
                    if (otsl4.Contains(ik) || otsl3.Contains(ik) || otsl2.Contains(ik) || otsl1.Contains(ik)) continue;
                    bool allContains = true;
                    foreach (string s in SubExpressions[ik].Includes)
                    {
                        if (!otsl4.Contains(s) && !otsl3.Contains(s) && !otsl2.Contains(s) && !otsl1.Contains(s))
                        {
                            allContains = false;
                            break;
                        }
                    }
                    if (allContains)
                    {
                        otsl5.Add(ik);
                        SubExpressions[ik].Rank = 5;
                    }
                }
            }
            if (otsl1.Count > 0) Ranks[1] = otsl1;
            if (otsl2.Count > 0) Ranks[2] = otsl2;
            if (otsl3.Count > 0) Ranks[3] = otsl3;
            if (otsl4.Count > 0) Ranks[4] = otsl4;
            if (otsl5.Count > 0) Ranks[5] = otsl5;

            string[] sortsSubExpressions = new string[SubExpressions.Count];
            int ii = 0;
            foreach (string ex in SubExpressions.Keys)
            {
                sortsSubExpressions[ii++] = ex;
            }
            for (int i = 0; i < sortsSubExpressions.Length - 1; i++)
            {
                for (int j = 0; j < sortsSubExpressions.Length - 1 - i; j++)
                {
                    if (SubExpressions[sortsSubExpressions[j]].Rank < SubExpressions[sortsSubExpressions[j + 1]].Rank)
                    {
                        var temp = sortsSubExpressions[j + 1];
                        sortsSubExpressions[j + 1] = sortsSubExpressions[j];
                        sortsSubExpressions[j] = temp;
                    }
                }
            }
            Dictionary<string, CompositeExpression> tempSubExpressions = new Dictionary<string, CompositeExpression>();
            foreach (string sex in sortsSubExpressions)
            {
                tempSubExpressions[sex] = SubExpressions[sex];
            }
            SubExpressions = tempSubExpressions;
            foreach (var ex in SubExpressions.Keys)
            {
                string[] tempIncludes = SubExpressions[ex].Includes.ToArray();
                for (int i = 0; i < tempIncludes.Length - 1; i++)
                {
                    for (int j = 0; j < tempIncludes.Length - 1 - i; j++)
                    {
                        if (SubExpressions[tempIncludes[j]].Rank < SubExpressions[tempIncludes[j + 1]].Rank)
                        {
                            var temp = tempIncludes[j + 1];
                            tempIncludes[j + 1] = tempIncludes[j];
                            tempIncludes[j] = temp;
                        }
                    }
                }
                SubExpressions[ex].Includes.Clear();
                foreach (string ti in tempIncludes)
                {
                    SubExpressions[ex].Includes.Add(ti);
                }
            }
        }

        //获取复杂函数的子表达式
        public void SetFuncExpression(string expression, string func)
        {
            if (expression.Contains(func))
            {
                List<int> indexs = new List<int>();
                int start = expression.IndexOf(func);
                while (start != -1)
                {
                    indexs.Add(start);
                    start = expression.IndexOf(func, start + 1);
                }
                if (func == "^" || func == "%")
                {
                    foreach (int index in indexs)
                    {
                        string leftExpression = string.Empty;
                        string rightExpression = string.Empty;
                        if (expression[index - 1] == ')')
                        {
                            int leftBracket = 0;
                            int rightBracket = 0;
                            for (int i = index; i >= 0; i--)
                            {
                                if (expression[i] == '(') leftBracket++;
                                if (expression[i] == ')') rightBracket++;
                                if (leftBracket == rightBracket && leftBracket != 0)
                                {
                                    leftExpression = expression.Substring(i, index - i);
                                    break;
                                }
                            }
                        }
                        else
                        {
                            for (int i = index - 1; i >= 0; i--)
                            {
                                if (!IsNum(expression[i]) && expression[i] != '.' && !IsVariables(expression[i], Variables))
                                    break;
                                leftExpression = expression[i] + leftExpression;
                            }
                        }
                        if (expression[index + 1] == '(')
                        {
                            int leftBracket = 0;
                            int rightBracket = 0;
                            for (int i = index; i < expression.Length; i++)
                            {
                                if (expression[i] == '(') leftBracket++;
                                if (expression[i] == ')') rightBracket++;
                                if (leftBracket == rightBracket && leftBracket != 0)
                                {
                                    rightExpression = expression.Substring(index + 1, i - index);
                                    break;
                                }
                            }
                        }
                        else
                        {
                            for (int i = index + 1; i < expression.Length; i++)
                            {
                                if (!IsNum(expression[i]) && expression[i] != '.' && !IsVariables(expression[i], Variables))
                                    break;
                                rightExpression = rightExpression + expression[i];
                            }
                        }
                        CompositeExpression composite = new CompositeExpression();
                        composite.Expression = leftExpression + func + rightExpression;
                        composite.Separations[0] = func;
                        composite.Separations[1] = leftExpression;
                        composite.Separations[2] = rightExpression;
                        SubExpressions[composite.Expression] = composite;
                    }
                }
                else
                {
                    foreach (int index in indexs)
                    {
                        int leftBracket = 0;
                        int rightBracket = 0;
                        for (int i = index; i < expression.Length; i++)
                        {
                            if (expression[i] == '(') leftBracket++;
                            if (expression[i] == ')') rightBracket++;
                            if (leftBracket == rightBracket && leftBracket != 0)
                            {
                                string subExpression = expression.Substring(index, i - index + 1);
                                CompositeExpression composite = new CompositeExpression();
                                composite.Expression = subExpression;
                                composite.Separations[0] = func;
                                composite.Separations[1] = composite.Expression.Substring(composite.Separations[0].Length + 1, composite.Expression.Length - composite.Separations[0].Length - 2);
                                SubExpressions[subExpression] = composite;
                                break;
                            }
                        }
                    }
                }
            }
        }

        //数值替换变量并计算
        public string CalculateVariableExpression(string expression, string[] variableChar, int[] variableValue)
        {
            if (variableChar.Length != variableValue.Length) return expression;
            if (expression.Contains("NaN")) return "NaN";
            for (int i = 0; i < variableChar.Length; i++)
            {
                if (variableValue[i] < 0)
                {
                    expression = expression.Replace(variableChar[i], "(0" + variableValue[i] + ")");
                }
                else
                {
                    expression = expression.Replace(variableChar[i], variableValue[i] + "");
                }
            }
            expression = Standardize(expression);
            if (IsNum(expression)) return expression;
            string epostfixExpression = GetEpostfixExpression(expression);
            string result = CalculateEpostfixExpression(epostfixExpression);
            return result;
        }

        //是否为数字
        public bool IsNum(char c)
        {
            foreach (char n in Nums)
            {
                if (c == n) return true;
            }
            return false;
        }

        //是否为数值
        public bool IsNum(string expression)
        {
            if (expression.StartsWith("-"))
            {
                expression = expression.Substring(1);
            }
            Regex regex = new Regex(@"^[0-9\.]+$");
            return !(string.IsNullOrEmpty(regex.Match(expression).Value));
        }

        //是否为变量
        public bool IsVariables(char c, string[] variables)
        {
            foreach (string v in variables)
            {
                if (char.Parse(v) == c) return true;
            }
            return false;
        }

        //是否为表达式
        public bool IsExpression(string expression)
        {
            Regex regex = new Regex(@"^[0-9\+\-\*\/\(\)\.]+$");
            return !(string.IsNullOrEmpty(regex.Match(expression).Value));
        }

        //表达式标准化
        public string Standardize(string expression)
        {
            expression = expression.Replace(" ", "");
            expression = expression.Replace("(-", "(0-");
            expression = expression.Replace("pi", MathUtils.PI.ToString());
            expression = expression.Replace("e", MathUtils.E.ToString());
            return expression;
        }

        //中缀转换成后缀表达式
        public string GetEpostfixExpression(string expression)
        {
            List<string> operatorList = new List<string>();
            string operatorStr;
            string epostfixExpression = "";
            string operandStr;
            while (expression.Length > 0)
            {
                operandStr = "";

                if (IsNum(expression[0]))
                {
                    while (IsNum(expression[0]) || expression[0] == '.')
                    {
                        operandStr += expression[0].ToString();
                        expression = expression.Substring(1);
                        if (expression == "") break;
                    }
                    epostfixExpression += operandStr + "|";
                }

                if (expression.Length > 0 && expression[0].ToString() == "(")
                {
                    operatorList.Add("(");
                    expression = expression.Substring(1);
                }

                operandStr = "";
                if (expression.Length > 0 && expression[0].ToString() == ")")
                {
                    do
                    {
                        if (operatorList[operatorList.Count - 1].ToString() != "(")
                        {
                            operandStr += operatorList[operatorList.Count - 1].ToString() + "|";
                            operatorList.RemoveAt(operatorList.Count - 1);
                        }
                        else
                        {
                            operatorList.RemoveAt(operatorList.Count - 1);
                            break;
                        }

                    }
                    while (true);
                    epostfixExpression += operandStr;
                    expression = expression.Substring(1);
                }

                operandStr = "";
                if (expression.Length > 0 && (expression[0].ToString() == "*" || expression[0].ToString() == "/" || expression[0].ToString() == "+" || expression[0].ToString() == "-"))
                {
                    operatorStr = expression[0].ToString();
                    if (operatorList.Count > 0)
                    {
                        if (operatorList[operatorList.Count - 1].ToString() == "(" || OperatorPriority(operatorStr, operatorList[operatorList.Count - 1].ToString()))
                        {
                            operatorList.Add(operatorStr);
                        }
                        else
                        {
                            operandStr += operatorList[operatorList.Count - 1].ToString() + "|";
                            operatorList.RemoveAt(operatorList.Count - 1);
                            operatorList.Add(operatorStr);
                            epostfixExpression += operandStr;
                        }
                    }
                    else
                    {
                        operatorList.Add(operatorStr);
                    }
                    expression = expression.Substring(1);
                }
            }
            operandStr = "";
            while (operatorList.Count != 0)
            {
                operandStr += operatorList[operatorList.Count - 1].ToString() + "|";
                operatorList.RemoveAt(operatorList.Count - 1);
            }
            if (operandStr.Length > 0)
            {
                epostfixExpression += operandStr.Substring(0, operandStr.Length - 1);
            }
            return epostfixExpression;
        }

        //计算后缀表达式
        public string CalculateEpostfixExpression(string epostfixExpression)
        {
            List<string> operandlist2 = new List<string>();
            float operand1;
            float operand2;
            string[] operandAr;
            epostfixExpression = epostfixExpression.Replace(" ", "");
            operandAr = epostfixExpression.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
            for (int i = 0; i < operandAr.Length; i++)
            {
                if (IsNum(operandAr[i][0]))
                {
                    operandlist2.Add(operandAr[i].ToString());
                }
                else
                {
                    operand2 = (float)double.Parse(operandlist2[operandlist2.Count - 1]);
                    operandlist2.RemoveAt(operandlist2.Count - 1);
                    operand1 = (float)double.Parse(operandlist2[operandlist2.Count - 1]);
                    operandlist2.RemoveAt(operandlist2.Count - 1);
                    operandlist2.Add(Arithmetic(operand1, operand2, operandAr[i]).ToString());
                }
            }
            return operandlist2[0].ToString();
        }

        //判断运算符优先级
        public bool OperatorPriority(string operator1, string operator2)
        {
            if (operator1 == "*" && operator2 == "+") return true;
            else if (operator1 == "*" && operator2 == "-") return true;
            else if (operator1 == "/" && operator2 == "+") return true;
            else if (operator1 == "/" && operator2 == "-") return true;
            else return false;
        }

        //四则运算
        public float Arithmetic(float operand1, float operand2, string str_operator)
        {
            switch (str_operator)
            {
                case "*": operand1 *= operand2; break;
                case "/": operand1 /= operand2; break;
                case "+": operand1 += operand2; break;
                case "-": operand1 -= operand2; break;
                default: break;
            }
            return operand1;
        }
    }
}